﻿using System;
using System.IO;

using System.Net.DnsToolkit.Packet;
using System.Net.DnsToolkit.Serializer;
using System.Net.DnsToolkit.SocketAsync;

namespace System.Net.DnsToolkit.Resolver {
    public class AsyncDnsSocket : IDnsSocket {


        private readonly IMessageWriter<Stream> _messageWriter;
        private readonly IMessageReader<Stream> _messageReader;
        private readonly UdpClientAsync _socket;
        private Action<DnsSocketEvent> _action;

        const int port = 53;

        public AsyncDnsSocket(
            IMessageWriter<Stream> messageWriter,
            IMessageReader<Stream> messageReader,
            UdpClientAsync socket) {

            _messageWriter = messageWriter;
            _messageReader = messageReader;
            _socket = socket;
            _socket.OnReceiveCompleted += OnReceiveCompleted;

        }

        void OnReceiveCompleted(object sender, UdpClientEventArgs e) {
            
            if (e.HasException) {
                
                e.Treated = true;
                Fail(); return;
            }

            var stream = new MemoryStream(e.Buffer, e.Offset, e.Length);
            
            IMessage message;
            bool read = _messageReader.TryRead(stream, out message);

            if (!read) {
                Fail(); return;
            }

            Success(message);

        }

        void Fail() {
            var dnsSocketEvent = new DnsSocketEvent {
                Response = null,
                Success = false
            };
            _action(dnsSocketEvent);
        }


        void Success(IMessage message) {
            var dnsSocketEvent = new DnsSocketEvent {
                Response = message,
                Success = true
            };
            _action(dnsSocketEvent);
        }


        public void Send(IPAddress address, IMessage message, Action<DnsSocketEvent> action) {

            _action = action;

            var remote = new IPEndPoint(address, port);
            var stream = new MemoryStream();
            _messageWriter.Write(stream, message);
            var bs = stream.ToArray();

            _socket.SendTo(remote, bs, 0, bs.Length, null);
            

        }

    }
}
